{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Enumerableモジュール\n",
    "\n",
    "* Enumerableモジュール\n",
    "    * ArrayクラスやHashクラスにインクルードされている\n",
    "    * <font color=\"red\">全てのメソッドがeachメソッドを元に定義されている</font>\n",
    "        * eachメソッドが定義されているクラスであれば、これらの多くのメソッドをそのクラスで利用可能"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## mapメソッド,collectメソッド\n",
    "\n",
    "与えられたブロックを評価した結果の配列を返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 4, 6]\n",
      "[:fruit, :drink]\n",
      "[\"apple\", \"coffee\"]\n"
     ]
    }
   ],
   "source": [
    "arr = [1, 2, 3]\n",
    "puts arr.map{|i| i * 2}\n",
    "\n",
    "hash = {fruit: \"apple\", drink: \"coffee\"}\n",
    "puts hash.collect{|key,value| key}\n",
    "puts hash.collect{|key,value| value}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## each_with_indexメソッド\n",
    "\n",
    "要素とそのインデックスをブロックに渡して繰り返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0: 10\n",
      "1: 11\n",
      "2: 12\n",
      "[10, 11, 12]\n",
      "0: [:fruit, \"apple\"]\n",
      "1: [:drink, \"coffee\"]\n",
      "{:fruit=>\"apple\", :drink=>\"coffee\"}\n"
     ]
    }
   ],
   "source": [
    "arr = [10, 11, 12]\n",
    "puts arr.each_with_index{|i, index| puts \"#{index}: #{i}\"}\n",
    "\n",
    "hash = {fruit: \"apple\", drink: \"coffee\"}\n",
    "puts hash.each_with_index{|key_value,index| puts\"#{index}: #{key_value}\"}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## injectメソッド,reduceメソッド\n",
    "\n",
    "* 自身の畳み込み演算を行う\n",
    "    * 初期値と自身の要素を順に組み合わせて結果を返す演算\n",
    "        * 初期値は引数でとる"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n",
      "34\n"
     ]
    }
   ],
   "source": [
    "puts [1,2,3].inject(0){|result, v| result + v * 2}\n",
    "# 0 + 1 * 2 => 2\n",
    "# 2 + 2 * 2 => 6\n",
    "# 6 + 3 * 2 => 12\n",
    "\n",
    "puts [2,4,6].reduce(10){|result, v| result + v * 2}\n",
    "# 10 + 2 * 2 => 14\n",
    "# 14 + 4 * 2 => 22\n",
    "# 22 + 6 * 2 => 34"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## each_sliceメソッド, each_consメソッド\n",
    "\n",
    "* each_slice\n",
    "    * 要素を指定された数で区切ってブロックに渡す\n",
    "        * <font color=\"red\">要素数が指定した数で割り切れない場合は最後だけ渡される数が少なくなる</font><font color=\"red\">\n",
    "* each_cons\n",
    "    * 先頭から要素を一つずつ選び、さらに余分に指定された数にあうように要素を選んでブロックに渡していく"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3]\n",
      "[4, 5]\n",
      "\n",
      "[1, 2, 3]\n",
      "[2, 3, 4]\n",
      "[3, 4, 5]\n"
     ]
    }
   ],
   "source": [
    "(1..5).each_slice(3){|i| puts i}\n",
    "puts\n",
    "\n",
    "(1..5).each_cons(3){|i| puts i}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## reverse_eachメソッド\n",
    "\n",
    "逆順にブロックに要素を渡していく"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "4\n",
      "3\n",
      "2\n",
      "1\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[1,2,3,4,5].reverse_each{|i| puts i}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## all?, any?, none?, one?メソッド\n",
    "\n",
    "* all?\n",
    "    * 全ての要素が真であればtrue\n",
    "* any?\n",
    "    * 真である要素が一つでもあればtrue\n",
    "* none?\n",
    "    * 全ての要素が偽であればtrue\n",
    "* one?\n",
    "    * 一つの要素だけが真であればtrue"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "all?\n",
      "true\n",
      "false\n",
      "\n",
      "any?\n",
      "true\n",
      "false\n",
      "\n",
      "none?\n",
      "false\n",
      "true\n",
      "\n",
      "one?\n",
      "true\n",
      "false\n"
     ]
    }
   ],
   "source": [
    "puts \"all?\"\n",
    "puts [1,2,3].all?\n",
    "puts [nil,2,3].all?\n",
    "puts\n",
    "\n",
    "puts \"any?\"\n",
    "puts [nil,nil,3].any?\n",
    "puts [nil,false,nil].any?\n",
    "puts\n",
    "\n",
    "puts \"none?\"\n",
    "puts [nil,nil,3].none?\n",
    "puts [nil,false,nil].none?\n",
    "puts\n",
    "\n",
    "puts \"one?\"\n",
    "puts [false,nil,3].one?\n",
    "puts [nil,false,nil].one?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## member?, include?メソッド\n",
    "\n",
    "* 指定された値と==メソッドがtrueになる要素があるときにtrueを返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "false\n",
      "\n",
      "false\n",
      "true\n",
      "\n"
     ]
    }
   ],
   "source": [
    "puts [1,2,3].member?(3)\n",
    "puts [1,2,3].member?(4)\n",
    "puts\n",
    "\n",
    "puts [1,2,3].include?(0)\n",
    "puts [1,2,3].include?(1)\n",
    "puts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## find, detect,find_index,find_all,select,reject,grepメソッド\n",
    "\n",
    "* find, detect\n",
    "    * ブロックを評価して<font color=\"red\">最初に</font>真となる要素を返す\n",
    "* find_index\n",
    "    * ブロックを評価して<font color=\"red\">最初に</font>真となるindexを返す\n",
    "* find_all, select\n",
    "    * ブロックの評価結果が真となるすべての要素を配列で返す\n",
    "* reject\n",
    "    * ブロックの評価結果が偽となるすべての要素を配列で返す\n",
    "* grep\n",
    "    * 指定したパターンとマッチする要素を含んだ配列を返す\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n",
      "2\n",
      "1\n",
      "[2, 4, 6]\n",
      "[2, 4, 6]\n",
      "[1, 3, 5]\n",
      "\n",
      "[1]\n",
      "[\"a\", \"b\", \"c\"]\n",
      "[:d]\n",
      "[1, 2, 3]\n",
      "[\"a\", \"b\", \"c\"]\n",
      "[1, 2, 3, \"a\", \"b\", \"c\", :d]\n"
     ]
    }
   ],
   "source": [
    "puts [1,2,3,4,5,6].find{|i| i % 2 == 0 }\n",
    "puts [1,2,3,4,5,6].detect{|i| i % 2 == 0 }\n",
    "puts [1,2,3,4,5,6].find_index{|i| i % 2 == 0 }\n",
    "puts [1,2,3,4,5,6].find_all{|i| i % 2 == 0 }\n",
    "puts [1,2,3,4,5,6].select{|i| i % 2 == 0 }\n",
    "puts [1,2,3,4,5,6].reject{|i| i % 2 == 0 }\n",
    "puts\n",
    "\n",
    "a = [1,2,3,\"a\",\"b\",\"c\",:d]\n",
    "puts a.grep(1)\n",
    "puts a.grep(/[a-c]/)\n",
    "puts a.grep(Symbol)\n",
    "puts a.grep(Numeric)\n",
    "puts a.grep(String)\n",
    "puts a.grep(Object)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## sortメソッド,sort_byメソッド\n",
    "\n",
    "* sort\n",
    "    * 要素を<=>メソッドで比較して昇順にソートした配列を新たに生成して返す\n",
    "    * ブロックを取る場合はブロックの評価結果を元にソートする\n",
    "* sort_by\n",
    "    * ブロックの評価結果を<=>で比較して、昇順にソートした配列を使って元の配列をソートした新しい配列を生成する"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4, 9, 3, 7, 8, 2, 6, 5, 1, 10]\n",
      "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
      "\n",
      "[\"a\", \"bb\", \"ccc\"]\n",
      "[\"ccc\", \"bb\", \"a\"]\n",
      "\n",
      "[\"cat\", \"mouse\", \"giraffe\", \"hippopotamus\"]\n",
      "\n",
      "Hashのソート\n",
      "値ソート\n",
      "[[\"David\", 40], [\"Alice\", 50], [\"Bob\", 60], [\"Carol\", 90]]\n",
      "keyソート\n",
      "[[\"Alice\", 50], [\"Bob\", 60], [\"Carol\", 90], [\"David\", 40]]\n"
     ]
    }
   ],
   "source": [
    "a = Array.new(10){|i|  1+ i}.shuffle\n",
    "puts a\n",
    "puts a.sort\n",
    "puts\n",
    "\n",
    "b = [\"a\", \"bb\", \"ccc\"]\n",
    "puts b.sort{|a, b| a.length <=> b.length}\n",
    "puts b.sort{|a, b| b.length <=> a.length}\n",
    "puts\n",
    "\n",
    "c = [\"mouse\", \"cat\", \"hippopotamus\", \"giraffe\"]\n",
    "puts c.sort_by {|a| a.size }\n",
    "puts\n",
    "\n",
    "puts \"Hashのソート\"\n",
    "d = { 'Carol' => 90, 'Alice' => 50, 'Bob' => 60, 'David' => 40 }\n",
    "puts \"値ソート\"\n",
    "puts d.sort_by {|k, v| v }\n",
    "puts \"keyソート\"\n",
    "puts d.sort_by {|k, v| k }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## max,minメソッド、max_by,min_byメソッド\n",
    "\n",
    "* max\n",
    "    * 要素の最大値を返す\n",
    "    * <=>で比較するため、全ての要素が対応している必要がある\n",
    "    * ブロックを渡すとブロックの評価結果を元に大小判定を行う\n",
    "* min\n",
    "    * 要素の最小値を返す\n",
    "    * <=>で比較するため、全ての要素が対応している必要がある\n",
    "    * ブロックを渡すとブロックの評価結果を元に大小判定を行う\n",
    "* max_by\n",
    "    * ブロックの評価結果が最大のものを返す\n",
    "* min_by\n",
    "    * ブロックの評価結果が最大のものを返す\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[2, 4, 6, 8, 5, 7, 9, 11, 13, 10]\n",
      "\n",
      "9の時が最大 (9 % 5 + 9 = 13)\n",
      "9\n",
      "9\n",
      "\n",
      "1の時が最小 (1 % 5 + 1 = 2)\n",
      "1\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "puts (1..10).map{|v| v % 5 + v}\n",
    "puts\n",
    "\n",
    "puts \"9の時が最大 (9 % 5 + 9 = 13)\"\n",
    "puts (1..10).max{|a,b| (a % 5 + a) <=> (b % 5 + b)}\n",
    "puts (1..10).max_by{|v| v % 5 + v}\n",
    "puts\n",
    "\n",
    "puts \"1の時が最小 (1 % 5 + 1 = 2)\"\n",
    "puts (1..10).min{|a,b| (a % 5 + a) <=> (b % 5 + b)}\n",
    "puts (1..10).min_by{|v| v % 5 + v}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## countメソッド,firstメソッド, takeメソッド, dropメソッド\n",
    "\n",
    "* count\n",
    "    * 要素数を返す\n",
    "* first\n",
    "    * 先頭の要素\n",
    "    * 引数があれば先頭からその数だけ要素を返す\n",
    "* take\n",
    "    * 引数で指定した数だけ先頭からその要素を返す\n",
    "    * 引数無しは不可\n",
    "* drop\n",
    "    * 引数で指定した数だけ先頭からその要素を取り除いた配列を返す\n",
    "    * 引数無しは不可"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "1\n",
      "[1, 2, 3]\n",
      "[1, 2, 3]\n",
      "wrong number of arguments (given 0, expected 1)\n",
      "[4, 5]\n",
      "wrong number of arguments (given 0, expected 1)\n"
     ]
    }
   ],
   "source": [
    "puts [1,2,3,4,5].count\n",
    "puts [1,2,3,4,5].first\n",
    "puts [1,2,3,4,5].first(3)\n",
    "puts [1,2,3,4,5].take(3)\n",
    "begin\n",
    "  puts [1,2,3,4,5].take\n",
    "rescue ArgumentError => e\n",
    "  puts e\n",
    "end\n",
    "\n",
    "puts [1,2,3,4,5].drop(3)\n",
    "begin\n",
    "  puts [1,2,3,4,5].drop\n",
    "rescue ArgumentError => e\n",
    "  puts e\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## take_whileメソッド, drop_whileメソッド\n",
    "\n",
    "* take_while\n",
    "    * 先頭からブロックを評価し、最初に偽となった要素の直前までを返す\n",
    "* drop_while\n",
    "    * 先頭からブロックを評価し、最初に偽となった要素の直前までを取り除いた配列を返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, :a, true, \"b\"]\n",
      "[nil, 5, false, true]\n"
     ]
    }
   ],
   "source": [
    "puts [1,2,:a,true,\"b\",nil,5,false,true].take_while{|v| v}\n",
    "puts [1,2,:a,true,\"b\",nil,5,false,true].drop_while{|v| v}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## cycleメソッド\n",
    "\n",
    "* cycle\n",
    "    * 要素を先頭から取り出し、末尾まで到達すると再度先頭に戻って繰り返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "[1,2,3].cycle do |v|\n",
    "  p v\n",
    "  break if v == 3\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## group_byメソッド\n",
    "\n",
    "* group_by\n",
    "    * ブロックの評価結果をキーとして同じキーを持つ要素を配列としたハッシュを返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1=>[1, 4, 7, 10], 2=>[2, 5, 8], 0=>[3, 6, 9]}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(1..10).group_by{|v| v % 3}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## zipメソッド\n",
    "\n",
    "* zip\n",
    "    * 自身と引数にした配列から1つずつ要素を取り出して配列を作り、それを要素とする配列を返す"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[:a, 1, \"a\"], [:b, 2, \"b\"], [:c, 3, nil]]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[:a, :b, :c].zip([1,2,3],[\"a\",\"b\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## lazyメソッド\n",
    "\n",
    "* lazy\n",
    "    * mapメソッドやselectメソッドなどが遅延評価を行うように再定義される\n",
    "        * 遅延評価になるとそれぞれのメソッドが配列ではなくEnumerable::Lazyを返す\n",
    "        * メソッドを評価するタイミングを遅らせられる\n",
    "            * <font color=\"red\">値が必要になるまで計算しないことによって、無限に続くリストも扱うことができる</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cycleがここで評価された場合は無限ループになってしまう\n",
      "#<Enumerator::Lazy:0x007f6794a6fe98>\n",
      "\n",
      "ここではじめて評価される\n",
      "[1, 2, 3, 1, 2, 3, 1]\n"
     ]
    }
   ],
   "source": [
    "puts \"cycleがここで評価された場合は無限ループになってしまう\"\n",
    "puts a = [1,2,3].lazy.cycle\n",
    "puts\n",
    "\n",
    "puts \"ここではじめて評価される\"\n",
    "puts a.first(7)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
